package cn.zn.flow.service.impl;

import cn.hutool.core.collection.CollUtil;
import cn.hutool.core.date.DateUtil;
import cn.hutool.core.util.BooleanUtil;
import cn.hutool.core.util.ObjectUtil;
import cn.hutool.core.util.StrUtil;
import cn.hutool.core.util.URLUtil;
import cn.hutool.http.HttpUtil;
import cn.hutool.json.JSONArray;
import cn.hutool.json.JSONObject;
import cn.hutool.json.JSONUtil;
import cn.zn.base.common.constant.Constants;
import cn.zn.base.common.utils.SpringContextUtil;
import cn.zn.base.exception.CustomException;
import cn.zn.components.CodeComponent;
import cn.zn.flow.entity.Flow;
import cn.zn.flow.entity.FlowNode;
import cn.zn.flow.mapper.FlowMapper;
import cn.zn.flow.mapper.FlowNodeMapper;
import cn.zn.flow.service.FlowService;
import cn.zn.flow.vo.FlowApproveVo;
import cn.zn.flow.vo.FlowPageVo;
import cn.zn.flowTemplate.edoc.FlowFinishService;
import cn.zn.flowTemplate.entity.SysFlowView;
import cn.zn.flowTemplate.enums.ApproveResultEnum;
import cn.zn.flowTemplate.mapper.SysFlowViewMapper;
import cn.zn.framework.utils.SecurityUtil;
import cn.zn.oss.entity.FileInfo;
import cn.zn.oss.service.FileInfoService;
import cn.zn.user.entity.SysUser;
import cn.zn.user.mapper.SysUserMapper;
import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;

import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;

/**
 * <p>
 * 审批流程 服务实现类
 * </p>
 *
 * @author zn-admin
 * @since 2021-06-28
 */
@Service
public class FlowServiceImpl extends ServiceImpl<FlowMapper, Flow> implements FlowService {

    @Autowired
    private CodeComponent codeComponent;

    @Autowired
    private FlowNodeMapper flowNodeMapper;

    @Autowired
    private SysFlowViewMapper flowViewMapper;

    @Autowired
    private FileInfoService fileInfoService;

    @Autowired
    private SysUserMapper userMapper;

    @Override
    @Transactional
    public String createFlow(String mainTable, String title, SysUser beginUser, JSONArray flowNodes) {
        if (CollUtil.isEmpty(flowNodes)) {
            throw new CustomException("流程不能为空!");
        }

        // 流程主表
        Flow flow = new Flow();

        String code = codeComponent.getCode("T_FLOW", "FLOW_SN", "", "yyyyMMddHHmm", 4);
        flow.setFlowSn(code);

        flow.setTitle(title);
        flow.setBeginUserCode(beginUser.getLoginName());
        flow.setBeginUserName(beginUser.getUserName());
        flow.setNowOrders(1);
        flow.setMaxOrders(flowNodes.size());
        flow.setMainTable(mainTable);
        flow.setFlowJson(flowNodes.toString());
        flow.setBeginTime(DateUtil.date());
        flow.setStatus("0");
        flow.insert();

        List<FlowNode> todoList = new ArrayList<>();

        // 保存审批节点
        String flowId = flow.getId();
        for (int i = 0; i < flowNodes.size(); i++) {
            JSONObject flowNode = flowNodes.getJSONObject(i);

            Integer orders = flowNode.getInt("orders", -1);
            JSONArray items = flowNode.getJSONArray("items");

            for (int j = 0; j < items.size(); j++) {
                JSONObject item = items.getJSONObject(j);

                FlowNode node = new FlowNode();
                node.setParentId(flowId);
                node.setNodeName(item.getStr("nodeName"));
                node.setOrders(orders);
                node.setSubOrders(j);
                node.setApproverCode(item.getStr("userCode"));
                node.setApproverName(item.getStr("userName"));
                node.setStatus("0");

                if (orders == 1) {
                    node.setReceiveTime(DateUtil.date());
                    node.setStatus("1");

                    // 添加待办列表
                    todoList.add(node);
                }
                node.insert();
            }
        }

        // 推送待办
        String sysName = StrUtil.split(mainTable, "_")[1];
        pushTodoList(todoList, flow.getBeginUserCode(), title, sysName);
        return flowId;
    }

    @Override
    @Transactional
    public void deleteFlowById(String id) {
        List<String> ids = new ArrayList<>();
        ids.add(id);
        deleteFlowById(ids);
    }

    @Override
    @Transactional
    public void deleteFlowById(List<String> ids) {
        if (CollUtil.isNotEmpty(ids)) {
            baseMapper.deleteFlowByIds(ids);

            List<FlowNode> flowNodes = flowNodeMapper.selectFlowNodeListByFlowIds(ids);
            if (CollUtil.isNotEmpty(flowNodes)) {
                finishErpTodo(flowNodes);
            }
        }
    }

    @Override
    public Flow selectFlowInfo(String id, Boolean edit) {
        Flow flow = baseMapper.selectById(id);
        if (flow != null) {
            SysUser user = userMapper.selectUserByCode(flow.getBeginUserCode());
            flow.setBeginUser(user);

            // 流程json
            JSONArray flowNodes = JSONUtil.parseArray(flow.getFlowJson());
            // 审批意见
            List<FlowNode> flowNodeList = flowNodeMapper.selectFlowNodeListByFlowId(id);

            if (BooleanUtil.isFalse(edit)) {
                setFlowApproveResult(flowNodes, flowNodeList);
            }

            flow.setFlowNodes(flowNodes);
            if (CollUtil.isNotEmpty(flowNodeList)) {
                for (FlowNode flowNode : flowNodeList) {
                    List<FileInfo> files = fileInfoService.selectFileInfoListByIds(flowNode.getAttachmentId());
                    flowNode.setAttachments(files);
                }
            }
            flow.setApprovalIdeas(flowNodeList);
        }
        return flow;
    }

    /**
     * 匹配审批结果
     *
     * @param flowNodes    流程json
     * @param flowNodeList 节点
     */
    private void setFlowApproveResult(JSONArray flowNodes, List<FlowNode> flowNodeList) {
        if (CollUtil.isEmpty(flowNodes)) {
            return;
        }

        for (int i = 0; i < flowNodes.size(); i++) {
            JSONObject flowNode = flowNodes.getJSONObject(i);
            Integer orders = flowNode.getInt("orders", -1);
            JSONArray items = flowNode.getJSONArray("items");
            for (int j = 0; j < items.size(); j++) {
                JSONObject item = items.getJSONObject(j);
                String userCode = item.getStr("userCode");

                SysUser user = userMapper.selectUserByCode(userCode);
                item.set("user", user);

                // 如果不是编辑，则获取审批意见
                if (CollUtil.isNotEmpty(flowNodeList)) {
                    for (FlowNode node : flowNodeList) {
                        if (ObjectUtil.equal(orders, node.getOrders()) && StrUtil.equals(userCode, node.getApproverCode()) && ObjectUtil.equal(j, node.getSubOrders())) {
                            item.set("approveResult", node.getApproveResult());
                            item.set("status", node.getStatus());
                            break;
                        }
                    }
                }
            }
        }
    }

    @Override
    public Object selectFlowListByUserCode(FlowPageVo vo) {
        SysUser user = SecurityUtil.getTheUser();
        vo.setUserCode(user.getLoginName());

        if (!vo.getPage()) {
            return baseMapper.selectFlowListByUserCode(vo);
        }

        Page<Flow> page = new Page<>(vo.getCurrent(), vo.getSize());
        return baseMapper.selectFlowListByUserCode(page, vo);
    }

    @Override
    @Transactional
    public void approve(String flowId, FlowApproveVo vo) {
        Flow flow = baseMapper.selectById(flowId);
        if (flow == null || StrUtil.equals("1", flow.getIsDel())) {
            throw new CustomException("该流程不存在，请刷新重试");
        }

        if (!flow.getNowOrders().equals(vo.getOrders())) {
            throw new CustomException("审批节点不一致，请刷新重试");
        }

        // 保存附件
        String fileIds = fileInfoService.insertFileInfos(null, vo.getAttachments());

        QueryWrapper<FlowNode> queryWrapper = new QueryWrapper<>();
        queryWrapper.eq("PARENT_ID", flowId);
        queryWrapper.eq("ORDERS", vo.getOrders());
        queryWrapper.eq("STATUS", "1");
        // 获取所有当前步骤节点
        List<FlowNode> finishNodes = flowNodeMapper.selectList(queryWrapper);
        if (CollUtil.isNotEmpty(finishNodes)) {
            // 保存签核内容
            FlowNode flowNode = new FlowNode();
            flowNode.setApproveResult(vo.getApproveResult());
            flowNode.setApproveIdea(vo.getApproveIdea());
            flowNode.setDeliverTime(DateUtil.date());
            flowNode.setAttachmentId(fileIds);
            flowNode.setStatus("2");
            flowNodeMapper.update(flowNode, queryWrapper);
        }

        List<FlowNode> todoList = new ArrayList<>();

        boolean finish = false;
        // 流转
        // 如果同意，则向下走
        if (StrUtil.equals("1", vo.getApproveResult())) {
            // 如果当前步骤全部审批完成
            if (checkOrdersApproveFinish(flowId, vo.getOrders())) {
                // 如果所有步骤都已审批完成
                if (checkAllApproveFinish(flowId)) {
                    finish = true;
                    finishFlow(flowId, flow.getMainTable(), "2");
                } else {
                    // 否则，往下一步转移
                    // 更新流程到下一步骤
                    Flow f = new Flow();
                    f.setId(flowId);
                    f.setNowOrders(flow.getNowOrders() + 1);
                    baseMapper.updateById(f);

                    // 更新业务表状态
                    baseMapper.updateTableFLowResult(flow.getMainTable(), flowId, "1");

                    // 移交下一步
                    List<FlowNode> nextNodes = flowNodeMapper.selectNextFlowNodeList(flowId, vo.getOrders() + 1);
                    if (CollUtil.isNotEmpty(nextNodes)) {
                        for (FlowNode nextNode : nextNodes) {
                            nextNode.setReceiveTime(DateUtil.date());
                            nextNode.setStatus("1");
                            flowNodeMapper.updateById(nextNode);
                        }
                        // 加入待办列表
                        todoList.addAll(nextNodes);
                    }
                }

                // 推送待办
                String sysName = StrUtil.split(flow.getMainTable(), "_")[1];
                pushTodoList(todoList, flow.getBeginUserCode(), flow.getTitle(), sysName);
            }
        } else {
            // 如果不同意，流程直接结束
            finish = true;
            finishFlow(flowId, flow.getMainTable(), "3");
        }

        if (finish) {
            SysFlowView flowView = flowViewMapper.selectFlowViewByTable(flow.getMainTable());
            if (flowView != null) {
                FlowFinishService flowFinishService = SpringContextUtil.getBean(flowView.getFlowService());
                ApproveResultEnum approveResult = ApproveResultEnum.parse(vo.getApproveResult());
                flowFinishService.doFlowFinish(flowId, approveResult);
            }
        }

        if (CollUtil.isNotEmpty(finishNodes)) {
            // 结束公文
            finishErpTodo(finishNodes);
        }
    }

    private void finishFlow(String flowId, String mainTable, String status) {
        // 更新流程状态
        Flow f = new Flow();
        f.setId(flowId);
        f.setStatus("1");
        f.setEndTime(DateUtil.date());
        baseMapper.updateById(f);

        // 更新业务表状态
        baseMapper.updateTableFLowResult(mainTable, flowId, status);
    }

    /**
     * 判断当前步骤是否审批完成
     *
     * @param flowId 流程id
     * @param orders 步骤
     * @return 结果
     */
    private boolean checkOrdersApproveFinish(String flowId, Integer orders) {
        Integer count = flowNodeMapper.countNoApproveFlowNode(flowId, orders);
        return count == 0;
    }

    /**
     * 判断所有步骤是否审批完成
     *
     * @param flowId 流程id
     * @return 结果
     */
    private boolean checkAllApproveFinish(String flowId) {
        Integer count = flowNodeMapper.countNoApproveFlowNode(flowId, null);
        return count == 0;
    }

    /**
     * 推送待办
     *
     * @param todoList 待办
     * @param title    标题
     * @param sysName  系统名
     */
    private void pushTodoList(List<FlowNode> todoList, String createUser, String title, String sysName) {
        if (CollUtil.isNotEmpty(todoList)) {
            SysUser user = userMapper.selectUserByCode(createUser);

            for (FlowNode node : todoList) {
                Map<String, Object> params = new HashMap<>();
                params.put("titile", title);
                params.put("createbyname", user.getUserName());
                params.put("createbycode", createUser);
                params.put("toname", node.getApproverName());
                params.put("tocode", node.getApproverCode());
                params.put("type", Constants.SSO_SALT);

                String ssoUrl = Constants.SYSTEM_SSO_URL.get(sysName);
                String url = StrUtil.indexedFormat(ssoUrl, URLUtil.encodeAll(StrUtil.indexedFormat(Constants.APPROVE_URL, node.getParentId(), node.getOrders(), node.getSubOrders())));
                params.put("url", url);
                params.put("phoneurl", url);

                params.put("company", user.getOrgName());
                String responseBody = HttpUtil.post(Constants.ERP_URL, JSONUtil.toJsonStr(params));
                JSONObject json = JSONUtil.parseObj(responseBody);
                if (json.getBool("success", false)) {
                    String erpId = json.getStr("id");
                    node.setErpTodoId(erpId);
                    flowNodeMapper.updateById(node);
                } else {
                    log.error("ERP推送失败:" + responseBody);
                }
            }
        }
    }

    /**
     * 处理待办公文
     *
     * @param finishNodes erpId
     */
    private void finishErpTodo(List<FlowNode> finishNodes) {
        for (FlowNode node : finishNodes) {
            if (StrUtil.isNotBlank(node.getErpTodoId())) {
                String responseBody = HttpUtil.get(StrUtil.indexedFormat(Constants.FINISH_ERP_ID, node.getErpTodoId()));
                JSONObject json = JSONUtil.parseObj(responseBody);
                if (!json.getBool("success", false)) {
                    log.error("ERP处理失败:" + node.getId() + "--" + responseBody);
                }
            } else {
                log.error("ERP处理失败:" + node.getId() + "--未找到ERP_ID");
            }
        }
    }
}
